using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;

namespace BodySoundSimulator.Training.CallibrationWizard
{
	public partial class CallibrationWizardDialog : Form
	{
		#region Fields
		Thread cameraCaptureThread;
		CallibrationData callibrationData;
		EditMode currentEditMode;
		Pen drawingPen = new Pen(Color.Red);
		Brush fillingBrush = new SolidBrush(Color.Red);
		Brush transparentFillingBrush = new SolidBrush(Color.FromArgb(50, Color.Red));
		float selectedPointWidth = 6;

		bool editingStarted;
		Point editStartPoint;
		Point editEndPoint;
		#endregion

		#region Delegates
		delegate void DisplayPushedImageDelegate(byte[] imageData, int width, int height, PictureBox picBox);
		#endregion

		#region Properties
		internal CallibrationData CallibrationData
		{
			get { return callibrationData; }
			set { callibrationData = value; }
		}
		#endregion

		#region Constructors
		public CallibrationWizardDialog()
		{
			InitializeComponent();
			this.tabPgeCenterMarker.Tag = EditMode.CenterMarkerBoundryEditMode;
			this.tabPgeLeftMarker.Tag = EditMode.LeftMarkerBoundryEditMode;
			this.tabPgeRightMarker.Tag = EditMode.RightMarkerBoundryEditMode;
			this.tabPgeStethMarker.Tag = EditMode.StethMarkerBoundryEditMode;
			this.tabPgeJugularNotch.Tag = EditMode.JugularNotchLocationEditMode;
			this.tabPgeLeftCostalMargin.Tag = EditMode.LeftCostalMarginEditMode;
			this.tabPgeRightCostalMargin.Tag = EditMode.RightCostalMarginEditMode;
			this.tabPgeXiphoidProcessLocation.Tag = EditMode.XiphoidProcessLocationEditMode;
			this.tabCtrlModes.SelectedTab = this.tabPgeCenterMarker;
			this.currentEditMode = EditMode.CenterMarkerBoundryEditMode;

			this.callibrationData = new CallibrationData();
			cameraCaptureThread = new Thread(new ThreadStart(StartCapturingFromCamera));
			cameraCaptureThread.Start();
		}
		#endregion

		#region UI event handlers
		private void btnOK_Click(object sender, EventArgs e)
		{
			byte[] callibrationDataBytes = this.callibrationData.GetBytes();
			int size = Marshal.SizeOf(callibrationDataBytes[0]) * callibrationDataBytes.Length;
			IntPtr pnt = Marshal.AllocHGlobal(size);
			try
			{
				// Copy the array to unmanaged memory.
				Marshal.Copy(callibrationDataBytes, 0, pnt, callibrationDataBytes.Length);
			}
			finally
			{
				//Free the unmanaged memory.
				//Marshal.FreeHGlobal(pnt);
			}
			OpenCvAdapterNativeMethods.UpdateDataAndStopCallibration(pnt, callibrationDataBytes.Length);
			Marshal.FreeHGlobal(pnt);
			this.DialogResult = DialogResult.OK;
		}

		private void btnCancel_Click(object sender, EventArgs e)
		{
			this.DialogResult = DialogResult.Cancel;
		}

		private void CallibrationWizardDialog_FormClosing(object sender, FormClosingEventArgs e)
		{
			OpenCvAdapterNativeMethods.StopCallibration();
		}

		private void tabCtrlModes_SelectedIndexChanged(object sender, EventArgs e)
		{
			this.currentEditMode = (EditMode)this.tabCtrlModes.SelectedTab.Tag;
		}

		private void picBoxSource_MouseDown(object sender, MouseEventArgs e)
		{
			switch (currentEditMode)
			{
				case EditMode.CenterMarkerBoundryEditMode:
				case EditMode.LeftMarkerBoundryEditMode:
				case EditMode.RightMarkerBoundryEditMode:
				case EditMode.StethMarkerBoundryEditMode:
					this.editingStarted = true;
					this.editStartPoint = new Point(e.X, e.Y);
					break;
				case EditMode.JugularNotchLocationEditMode:
					this.callibrationData.JugularNotchLocation = new Point(e.X, e.Y);
					break;
				case EditMode.XiphoidProcessLocationEditMode:
					this.callibrationData.XiphoidProcessLocation = new Point(e.X, e.Y);
					break;
				case EditMode.LeftCostalMarginEditMode:
					this.callibrationData.LeftCostalMarginLocation = new Point(e.X, e.Y);
					break;
				case EditMode.RightCostalMarginEditMode:
					this.callibrationData.RightCostalMarginLocation = new Point(e.X, e.Y);
					break;
			}
		}

		private void picBoxSource_MouseMove(object sender, MouseEventArgs e)
		{
			if (editingStarted)
			{
				switch (currentEditMode)
				{
					case EditMode.CenterMarkerBoundryEditMode:
					case EditMode.LeftMarkerBoundryEditMode:
					case EditMode.RightMarkerBoundryEditMode:
					case EditMode.StethMarkerBoundryEditMode:
						this.editEndPoint = new Point(e.X, e.Y);
						break;
				}
			}
		}

		private void picBoxSource_MouseUp(object sender, MouseEventArgs e)
		{
			if (editingStarted)
			{
				int width = Math.Abs(this.editStartPoint.X - this.editEndPoint.X);
				int height = Math.Abs(this.editStartPoint.Y - this.editEndPoint.Y);
				Rectangle rect = new Rectangle(this.editStartPoint.X,
								this.editStartPoint.Y,
								width,
								height);
				switch (this.currentEditMode)
				{
					case EditMode.CenterMarkerBoundryEditMode:
						this.callibrationData.CenterMarkerBoundry = rect;
						break;
					case EditMode.LeftMarkerBoundryEditMode:
						this.callibrationData.LeftMarkerBoundry = rect;
						break;
					case EditMode.RightMarkerBoundryEditMode:
						this.callibrationData.RightMarkerBoundry = rect;
						break;
					case EditMode.StethMarkerBoundryEditMode:
						this.callibrationData.StethMarkerBoundry = rect;
						break;
				}
				editingStarted = false;
			}
		}
		#endregion

		#region Private methods
		private void StartCapturingFromCamera()
		{
			OpenCvAdapterNativeMethods.StartCallibration(new OpenCvAdapterNativeMethods.ImagePushedDelegate(SourceImagePushed));
		}

		private void SourceImagePushed(int width, int height, IntPtr imageDataPointer)
		{
			DisplayPushedImageOnControl(width, height, imageDataPointer, this.picBoxSource);
		}

		private void DisplayPushedImageOnControl(int width, int height, IntPtr imageDataPointer, PictureBox picBox)
		{
			int size = height * width;
			byte[] imageData = new byte[size];
			Marshal.Copy(imageDataPointer, imageData, 0, size);
			if (picBox.InvokeRequired)
			{
				picBox.BeginInvoke(new DisplayPushedImageDelegate(DisplayPushedImage), imageData, width, height, picBox);
			}
			else
			{
				DisplayPushedImage(imageData, width, height, picBox);
			}
		}

		private void DisplayPushedImage(byte[] imageData, int width, int height, PictureBox picBox)
		{
			this.picBoxSource.Image = BitmapCreationHelper.GetBitmapFromPixelData(imageData, width, height);
			this.picBoxSource.Refresh();
		}

		private void picBoxSource_Paint(object sender, PaintEventArgs e)
		{
			Graphics g = e.Graphics;
			if (this.editingStarted)
			{
				g.FillRectangle(transparentFillingBrush, new Rectangle(this.editStartPoint.X,
								this.editStartPoint.Y,
								Math.Abs(this.editStartPoint.X - this.editEndPoint.X),
								Math.Abs(this.editStartPoint.Y - this.editEndPoint.Y)));
			}
			else
			{
				switch (currentEditMode)
				{
					case EditMode.CenterMarkerBoundryEditMode:
						g.FillRectangle(transparentFillingBrush, this.callibrationData.CenterMarkerBoundry);
						break;
					case EditMode.LeftMarkerBoundryEditMode:
						g.FillRectangle(transparentFillingBrush, this.callibrationData.LeftMarkerBoundry);
						break;
					case EditMode.RightMarkerBoundryEditMode:
						g.FillRectangle(transparentFillingBrush, this.callibrationData.RightMarkerBoundry);
						break;
					case EditMode.StethMarkerBoundryEditMode:
						g.FillRectangle(transparentFillingBrush, this.callibrationData.StethMarkerBoundry);
						break;
					case EditMode.JugularNotchLocationEditMode:
						g.FillEllipse(fillingBrush,
							this.callibrationData.JugularNotchLocation.X - this.selectedPointWidth / 2,
							this.callibrationData.JugularNotchLocation.Y - this.selectedPointWidth / 2,
							this.selectedPointWidth,
							this.selectedPointWidth);
						break;
					case EditMode.XiphoidProcessLocationEditMode:
						g.FillEllipse(fillingBrush,
							this.callibrationData.XiphoidProcessLocation.X - this.selectedPointWidth / 2,
							this.callibrationData.XiphoidProcessLocation.Y - this.selectedPointWidth / 2,
							this.selectedPointWidth,
							this.selectedPointWidth);
						break;
					case EditMode.LeftCostalMarginEditMode:
						g.FillEllipse(fillingBrush,
							this.callibrationData.LeftCostalMarginLocation.X - this.selectedPointWidth / 2,
							this.callibrationData.LeftCostalMarginLocation.Y - this.selectedPointWidth / 2,
							this.selectedPointWidth,
							this.selectedPointWidth);
						break;
					case EditMode.RightCostalMarginEditMode:
						g.FillEllipse(fillingBrush,
							this.callibrationData.RightCostalMarginLocation.X - this.selectedPointWidth / 2,
							this.callibrationData.RightCostalMarginLocation.Y - this.selectedPointWidth / 2,
							this.selectedPointWidth,
							this.selectedPointWidth);
						break;

				}
			}
		}
		#endregion
	}
}